home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / glibc-1.09 / glibc-1 / glibc-1.09.1 / stdio / __vfscanf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-10  |  12.4 KB  |  563 lines

  1. /* Copyright (C) 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
  2. This file is part of the GNU C Library.
  3.  
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public License as
  6. published by the Free Software Foundation; either version 2 of the
  7. License, or (at your option) any later version.
  8.  
  9. The GNU C Library is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12. Library General Public License for more details.
  13.  
  14. You should have received a copy of the GNU Library General Public
  15. License along with the GNU C Library; see the file COPYING.LIB.  If
  16. not, write to the Free Software Foundation, Inc., 675 Mass Ave,
  17. Cambridge, MA 02139, USA.  */
  18.  
  19. #include <ansidecl.h>
  20. #include <localeinfo.h>
  21. #include <errno.h>
  22. #include <limits.h>
  23. #include <ctype.h>
  24. #include <stdarg.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28.  
  29.  
  30. #ifdef    __GNUC__
  31. #define    HAVE_LONGLONG
  32. #define    LONGLONG    long long
  33. #else
  34. #define    LONGLONG    long
  35. #endif
  36.  
  37.  
  38. #define    inchar()    ((c = getc(s)) == EOF ? EOF : (++read_in, c))
  39. #define    conv_error()    return ((c == EOF || ungetc(c, s)), done)
  40. #define input_error()    return (done == 0 ? EOF : done)
  41. #define    memory_error()    return ((errno = ENOMEM), EOF)
  42.  
  43.  
  44. /* Read formatted input from S according to the format string
  45.    FORMAT, using the argument list in ARG.
  46.    Return the number of assignments made, or -1 for an input error.  */
  47. int
  48. DEFUN(__vfscanf, (s, format, arg),
  49.       FILE *s AND CONST char *format AND va_list argptr)
  50. {
  51.   va_list arg = (va_list) argptr;
  52.  
  53.   register CONST char *f = format;
  54.   register char fc;        /* Current character of the format.  */
  55.   register size_t done = 0;    /* Assignments done.  */
  56.   register size_t read_in = 0;    /* Chars read in.  */
  57.   register int c;        /* Last char read.  */
  58.   register int do_assign;    /* Whether to do an assignment.  */
  59.   register int width;        /* Maximum field width.  */
  60.  
  61.   /* Type modifiers.  */
  62.   char is_short, is_long, is_long_double;
  63. #ifdef    HAVE_LONGLONG
  64.   /* We use the `L' modifier for `long long int'.  */
  65. #define    is_longlong    is_long_double
  66. #else
  67. #define    is_longlong    0
  68. #endif
  69.   int malloc_string;        /* Args are char ** to be filled in.  */
  70.   /* Status for reading F-P nums.  */
  71.   char got_dot, got_e;
  72.   /* If a [...] is a [^...].  */
  73.   char not_in;
  74.   /* Base for integral numbers.  */
  75.   int base;
  76.   /* Signedness for integral numbers.  */
  77.   int number_signed;
  78.   /* Integral holding variables.  */
  79.   long int num;
  80.   unsigned long int unum;
  81.   /* Floating-point holding variable.  */
  82.   LONG_DOUBLE fp_num;
  83.   /* Character-buffer pointer.  */
  84.   register char *str, **strptr;
  85.   size_t strsize;
  86.   /* Workspace.  */
  87.   char work[200];
  88.   char *w;            /* Pointer into WORK.  */
  89.   wchar_t decimal;        /* Decimal point character.  */
  90.  
  91.   if (!__validfp(s) || !s->__mode.__read || format == NULL)
  92.     {
  93.       errno = EINVAL;
  94.       return EOF;
  95.     }
  96.  
  97.   /* Figure out the decimal point character.  */
  98.   if (mbtowc(&decimal, _numeric_info->decimal_point,
  99.          strlen(_numeric_info->decimal_point)) <= 0)
  100.     decimal = (wchar_t) *_numeric_info->decimal_point;
  101.  
  102.   c = inchar();
  103.  
  104.   /* Run through the format string.  */
  105.   while (*f != '\0')
  106.     {
  107.       if (!isascii(*f))
  108.     {
  109.       /* Non-ASCII, may be a multibyte.  */
  110.       int len = mblen(f, strlen(f));
  111.       if (len > 0)
  112.         {
  113.           while (len-- > 0)
  114.         if (c == EOF)
  115.           input_error();
  116.         else if (c == *f++)
  117.           (void) inchar();
  118.         else
  119.           conv_error();
  120.           continue;
  121.         }
  122.     }
  123.  
  124.       fc = *f++;
  125.       if (fc != '%')
  126.     {
  127.       /* Characters other than format specs must just match.  */
  128.       if (c == EOF)
  129.         input_error();
  130.       if (isspace(fc))
  131.         {
  132.           /* Whitespace characters match any amount of whitespace.  */
  133.           while (isspace (c))
  134.         inchar ();
  135.           continue;
  136.         }
  137.       else if (c == fc)
  138.         (void) inchar();
  139.       else
  140.         conv_error();
  141.       continue;
  142.     }
  143.  
  144.       /* Check for the assignment-suppressant.  */
  145.       if (*f == '*')
  146.     {
  147.       do_assign = 0;
  148.       ++f;
  149.     }
  150.       else
  151.     do_assign = 1;
  152.         
  153.       /* Find the maximum field width.  */
  154.       width = 0;
  155.       while (isdigit(*f))
  156.     {
  157.       width *= 10;
  158.       width += *f++ - '0';
  159.     }
  160.       if (width == 0)
  161.     width = -1;
  162.  
  163.       /* Check for type modifiers.  */
  164.       is_short = is_long = is_long_double = malloc_string = 0;
  165.       while (*f == 'h' || *f == 'l' || *f == 'L' || *f == 'a' || *f == 'q')
  166.     switch (*f++)
  167.       {
  168.       case 'h':
  169.         /* int's are short int's.  */
  170.         is_short = 1;
  171.         break;
  172.       case 'l':
  173.         if (is_long)
  174.           /* A double `l' is equivalent to an `L'.  */
  175.           is_longlong = 1;
  176.         else
  177.           /* int's are long int's.  */
  178.           is_long = 1;
  179.         break;
  180.       case 'q':
  181.       case 'L':
  182.         /* double's are long double's, and int's are long long int's.  */
  183.         is_long_double = 1;
  184.         break;
  185.       case 'a':
  186.         /* String conversions (%s, %[) take a `char **'
  187.            arg and fill it in with a malloc'd pointer.  */
  188.         malloc_string = 1;
  189.         break;
  190.       }
  191.  
  192.       /* End of the format string?  */
  193.       if (*f == '\0')
  194.     conv_error();
  195.  
  196.       /* Find the conversion specifier.  */
  197.       w = work;
  198.       fc = *f++;
  199.       if (fc != '[' && fc != 'c' && fc != 'n')
  200.     /* Eat whitespace.  */
  201.     while (isspace(c))
  202.       (void) inchar();
  203.       switch (fc)
  204.     {
  205.     case '%':    /* Must match a literal '%'.  */
  206.       if (c != fc)
  207.         conv_error();
  208.       break;
  209.  
  210.     case 'n':    /* Answer number of assignments done.  */
  211.       if (do_assign)
  212.         *va_arg(arg, int *) = read_in;
  213.       break;
  214.  
  215.     case 'c':    /* Match characters.  */
  216.       if (do_assign)
  217.         {
  218.           str = va_arg (arg, char *);
  219.           if (str == NULL)
  220.         conv_error ();
  221.         }
  222.  
  223.       if (c == EOF)
  224.         input_error();
  225.  
  226.       if (width == -1)
  227.         width = 1;
  228.  
  229.       if (do_assign)
  230.         {
  231.           do
  232.         *str++ = c;
  233.           while (inchar() != EOF && --width > 0);
  234.         }
  235.       else
  236.         while (inchar() != EOF && width > 0)
  237.           --width;
  238.  
  239.       if (do_assign)
  240.         ++done;
  241.  
  242.       break;
  243.  
  244.     case 's':        /* Read a string.  */
  245. #define STRING_ARG                                  \
  246.       if (do_assign)                              \
  247.         {                                      \
  248.           if (malloc_string)                          \
  249.         {                                  \
  250.           /* The string is to be stored in a malloc'd buffer.  */     \
  251.           strptr = va_arg (arg, char **);                  \
  252.           if (strptr == NULL)                          \
  253.             conv_error ();                          \
  254.           /* Allocate an initial buffer.  */                  \
  255.           strsize = 100;                          \
  256.           *strptr = str = malloc (strsize);                  \
  257.         }                                  \
  258.           else                                  \
  259.         str = va_arg (arg, char *);                      \
  260.           if (str == NULL)                              \
  261.         conv_error ();                              \
  262.         }
  263.       STRING_ARG;
  264.  
  265.       if (c == EOF)
  266.         input_error ();
  267.  
  268.       do
  269.         {
  270.           if (isspace (c))
  271.         break;
  272. #define    STRING_ADD_CHAR(c)                              \
  273.           if (do_assign)                              \
  274.         {                                  \
  275.           *str++ = c;                              \
  276.           if (malloc_string && str == *strptr + strsize)          \
  277.             {                                  \
  278.               /* Enlarge the buffer.  */                  \
  279.               str = realloc (*strptr, strsize * 2);              \
  280.               if (str == NULL)                          \
  281.             {                              \
  282.               /* Can't allocate that much.  Last-ditch effort.  */\
  283.               str = realloc (*strptr, strsize + 1);              \
  284.               if (str == NULL)                      \
  285.                 {                              \
  286.                   /* We lose.  Oh well.                  \
  287.                  Terminate the string and stop converting,    \
  288.                  so at least we don't swallow any input.  */  \
  289.                   (*strptr)[strsize] = '\0';              \
  290.                   ++done;                          \
  291.                   conv_error ();                      \
  292.                 }                              \
  293.               else                              \
  294.                 {                              \
  295.                   *strptr = str;                      \
  296.                   str += strsize;                      \
  297.                   ++strsize;                      \
  298.                 }                              \
  299.             }                              \
  300.               else                              \
  301.             {                              \
  302.               *strptr = str;                      \
  303.               str += strsize;                      \
  304.               strsize *= 2;                          \
  305.             }                              \
  306.             }                                  \
  307.         }
  308.           STRING_ADD_CHAR (c);
  309.         } while (inchar () != EOF && (width <= 0 || --width > 0));
  310.  
  311.       if (do_assign)
  312.         {
  313.           *str = '\0';
  314.           ++done;
  315.         }
  316.       break;
  317.  
  318.     case 'x':    /* Hexadecimal integer.  */
  319.     case 'X':    /* Ditto.  */ 
  320.       base = 16;
  321.       number_signed = 0;
  322.       goto number;
  323.  
  324.     case 'o':    /* Octal integer.  */
  325.       base = 8;
  326.       number_signed = 0;
  327.       goto number;
  328.  
  329.     case 'u':    /* Unsigned decimal integer.  */
  330.       base = 10;
  331.       number_signed = 0;
  332.       goto number;
  333.  
  334.     case 'd':    /* Signed decimal integer.  */
  335.       base = 10;
  336.       number_signed = 1;
  337.       goto number;
  338.  
  339.     case 'i':    /* Generic number.  */
  340.       base = 0;
  341.       number_signed = 1;
  342.  
  343.     number:
  344.       if (c == EOF)
  345.         input_error();
  346.  
  347.       /* Check for a sign.  */
  348.       if (c == '-' || c == '+')
  349.         {
  350.           *w++ = c;
  351.           if (width > 0)
  352.         --width;
  353.           (void) inchar();
  354.         }
  355.  
  356.       /* Look for a leading indication of base.  */
  357.       if (c == '0')
  358.         {
  359.           if (width > 0)
  360.         --width;
  361.           *w++ = '0';
  362.  
  363.           (void) inchar();
  364.  
  365.           if (tolower(c) == 'x')
  366.         {
  367.           if (base == 0)
  368.             base = 16;
  369.           if (base == 16)
  370.             {
  371.               if (width > 0)
  372.             --width;
  373.               (void) inchar();
  374.             }
  375.         }
  376.           else if (base == 0)
  377.         base = 8;
  378.         }
  379.  
  380.       if (base == 0)
  381.         base = 10;
  382.  
  383.       /* Read the number into WORK.  */
  384.       do
  385.         {
  386.           if (base == 16 ? !isxdigit(c) :
  387.           (!isdigit(c) || c - '0' >= base))
  388.         break;
  389.           *w++ = c;
  390.           if (width > 0)
  391.         --width;
  392.         } while (inchar() != EOF && width != 0);
  393.  
  394.       if (w == work ||
  395.           (w - work == 1 && (work[0] == '+' || work[0] == '-')))
  396.         /* There was on number.  */
  397.         conv_error();
  398.  
  399.       /* Convert the number.  */
  400.       *w = '\0';
  401.       if (number_signed)
  402.         num = strtol (work, &w, base);
  403.       else
  404.         unum = strtoul (work, &w, base);
  405.       if (w == work)
  406.         conv_error ();
  407.  
  408.       if (do_assign)
  409.         {
  410.           if (! number_signed)
  411.         {
  412.           if (is_longlong)
  413.             *va_arg (arg, unsigned LONGLONG int *) = unum;
  414.           else if (is_long)
  415.             *va_arg (arg, unsigned long int *) = unum;
  416.           else if (is_short)
  417.             *va_arg (arg, unsigned short int *)
  418.               = (unsigned short int) unum;
  419.           else
  420.             *va_arg(arg, unsigned int *) = (unsigned int) unum;
  421.         }
  422.           else
  423.         {
  424.           if (is_longlong)
  425.             *va_arg(arg, LONGLONG int *) = num;
  426.           else if (is_long)
  427.             *va_arg(arg, long int *) = num;
  428.           else if (is_short)
  429.             *va_arg(arg, short int *) = (short int) num;
  430.           else
  431.             *va_arg(arg, int *) = (int) num;
  432.         }
  433.           ++done;
  434.         }
  435.       break;
  436.  
  437.     case 'e':    /* Floating-point numbers.  */
  438.     case 'E':
  439.     case 'f':
  440.     case 'g':
  441.     case 'G':
  442.       if (c == EOF)
  443.         input_error();
  444.  
  445.       /* Check for a sign.  */
  446.       if (c == '-' || c == '+')
  447.         {
  448.           *w++ = c;
  449.           if (inchar() == EOF)
  450.         /* EOF is only an input error before we read any chars.  */
  451.         conv_error();
  452.           if (width > 0)
  453.         --width;
  454.         }
  455.  
  456.       got_dot = got_e = 0;
  457.       do
  458.         {
  459.           if (isdigit(c))
  460.         *w++ = c;
  461.           else if (got_e && w[-1] == 'e' && (c == '-' || c == '+'))
  462.         *w++ = c;
  463.           else if (!got_e && tolower(c) == 'e')
  464.         {
  465.           *w++ = 'e';
  466.           got_e = got_dot = 1;
  467.         }
  468.           else if (c == decimal && !got_dot)
  469.         {
  470.           *w++ = c;
  471.           got_dot = 1;
  472.         }
  473.           else
  474.         break;
  475.           if (width > 0)
  476.         --width;
  477.         } while (inchar() != EOF && width != 0);
  478.  
  479.       if (w == work)
  480.         conv_error();
  481.       if (w[-1] == '-' || w[-1] == '+' || w[-1] == 'e')
  482.         conv_error();
  483.  
  484. #ifndef MIB_HACKS
  485.       /* Convert the number.  */
  486.       *w = '\0';
  487.       fp_num = strtod(work, &w);
  488.       if (w == work)
  489.         conv_error();
  490.  
  491.       if (do_assign)
  492.         {
  493.           if (is_long_double)
  494.         *va_arg(arg, LONG_DOUBLE *) = fp_num;
  495.           else if (is_long)
  496.         *va_arg(arg, double *) = (double) fp_num;
  497.           else
  498.         *va_arg(arg, float *) = (float) fp_num;
  499.           ++done;
  500.         }
  501.       break;
  502. #endif /* MIB_HACKS */
  503.  
  504.     case '[':    /* Character class.  */
  505.       STRING_ARG;
  506.  
  507.       if (c == EOF)
  508.         input_error();
  509.  
  510.       if (*f == '^')
  511.         {
  512.           ++f;
  513.           not_in = 1;
  514.         }
  515.       else
  516.         not_in = 0;
  517.  
  518.       while ((fc = *f++) != '\0' && fc != ']')
  519.         {
  520.           if (fc == '-' && *f != '\0' && *f != ']' &&
  521.           w > work && w[-1] <= *f)
  522.         /* Add all characters from the one before the '-'
  523.            up to (but not including) the next format char.  */
  524.         for (fc = w[-1] + 1; fc < *f; ++fc)
  525.           *w++ = fc;
  526.           else
  527.         /* Add the character to the list.  */
  528.         *w++ = fc;
  529.         }
  530.       if (fc == '\0')
  531.         conv_error();
  532.  
  533.       *w = '\0';
  534.       unum = read_in;
  535.       do
  536.         {
  537.           if ((strchr (work, c) == NULL) != not_in)
  538.         break;
  539.           STRING_ADD_CHAR (c);
  540.           if (width > 0)
  541.         --width;
  542.         } while (inchar () != EOF && width != 0);
  543.       if (read_in == unum)
  544.         conv_error ();
  545.  
  546.       if (do_assign)
  547.         {
  548.           *str = '\0';
  549.           ++done;
  550.         }
  551.       break;
  552.  
  553.     case 'p':    /* Generic pointer.  */
  554.       base = 16;
  555.       /* A PTR must be the same size as a `long int'.  */
  556.       is_long = 1;
  557.       goto number;
  558.     }
  559.     }
  560.  
  561.   conv_error();
  562. }
  563.